/*
 * Copyright (c) 2024 <mewiteor@hotmail.com>
 * Monitor-Demo is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan
 * PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * 
 * @file: cpu usage.c
 * @brief: 总的 CPU 使用率
 * 每秒显示一次 CPU 使用率。
 * 三种获取方法：
 * 1. GetSystemTimes() 系统调用
 * 2. NtQuerySystemInformation() 系统调用 (与GetSystemTimes()相同)
 * 3. pdh 动态链接库
 * 
 * @author: mewiteor
 * @email: mewiteor@hotmail.com
 * @date: 2024-05-26
 * @version: 1.0.0.0
 * @license: Mulan PSL v2
 */
#include <Windows.h>
#include <stdio.h>
#include <stdint.h>
#include <conio.h>
#include <pdh.h>

double GetBySystemTimes();
double GetByPdh();

int main()
{
    while(_kbhit()) _getch();
    while(!_kbhit())
    {
        double usage_by_system_times = GetBySystemTimes();
        double usage_by_pdh = GetByPdh();
        system("cls");
        printf("System Times: %5.1f%%\n", usage_by_system_times);
        printf("pdh         : %5.1f%%\n", usage_by_pdh);

        Sleep(1000);
    }
    while(_kbhit()) _getch();
    return 0;
}

double GetBySystemTimes()
{
    static uint64_t cpuLastTime = 0;
    static uint64_t cpuLastIdleTime;
    uint64_t kernelTime;
    uint64_t userTime;
    uint64_t idleTime;
    uint64_t allTime;
    double usage = -1;

    GetSystemTimes((LPFILETIME)&idleTime, (LPFILETIME)&kernelTime, (LPFILETIME)&userTime);
    allTime = kernelTime + userTime;
    if(cpuLastTime)
    {
        uint64_t deltaAllTime = allTime - cpuLastTime;
        if (deltaAllTime)
        {
            uint64_t deltaIdleTime = idleTime - cpuLastIdleTime;
            usage = (deltaAllTime - deltaIdleTime) * 100.0 / deltaAllTime;
        }
    }
    cpuLastTime = allTime;
    cpuLastIdleTime = idleTime;

    return usage;
}

double GetByPdh()
{
	HQUERY hQuery;
	HCOUNTER hCounter;
	DWORD counterType;
	PDH_RAW_COUNTER rawData;
    static PDH_RAW_COUNTER lastRawData;
    static BOOL first_time_run = TRUE;
    PDH_FMT_COUNTERVALUE fmtValue;
    static DWORD dwMajorVer = 0;
    fmtValue.doubleValue = -1;

    PdhOpenQueryW(NULL, 0, &hQuery);//开始查询
    LPCWSTR query_str;
    if (!dwMajorVer)
    {
        OSVERSIONINFOW osinfo;
        GetVersionExW(&osinfo);
        dwMajorVer = osinfo.dwMajorVersion;
    }
    if(dwMajorVer >= 10)
		query_str = L"\\Processor Information(_Total)\\% Processor Utility";
	else
		query_str = L"\\Processor Information(_Total)\\% Processor Time";
    PdhAddCounterW(hQuery, query_str, (DWORD_PTR)NULL, &hCounter);
	PdhCollectQueryData(hQuery);
	PdhGetRawCounterValue(hCounter, &counterType, &rawData);

    if(!first_time_run)//需要获得两次数据才能计算CPU使用率
        PdhCalculateCounterFromRawValue(hCounter, PDH_FMT_DOUBLE, &rawData, &lastRawData, &fmtValue);//计算使用率
	PdhCloseQuery(hQuery);//关闭查询

    lastRawData = rawData;//保存上一次数据
    first_time_run = FALSE;

    return fmtValue.doubleValue;
}